<script lang="ts" setup>
  import {
    AppstoreAddOutlined,
    DeleteOutlined,
    ExportOutlined,
    PlusOutlined,
    ToolOutlined,
    UploadOutlined,
  } from '@ant-design/icons-vue';
  import http from '@/store/http';
  import { computed, reactive, ref } from 'vue';
  import router from '@/router';
  import { Response } from '@/types';
  import { FormInstance, message, TableProps, UploadChangeParam, UploadFile } from 'ant-design-vue';
  import * as XLSX from 'xlsx';
  /* load 'fs' for readFile and writeFile support */
  import * as fs from 'fs';

  XLSX.set_fs(fs);

  const formName = router.currentRoute.value.fullPath.split('/').reverse()[0];

  interface Form {
    id?: number;
    name: string;
  }

  const form = ref<Form>({
    name: formName,
  });

  export interface Column {
    id?: number,
    title?: string;
    key?: string;
    dataIndex?: string;
    type?: string;
    formula?: string;
    required?: boolean;
    width?: number;
    align?: 'left' | 'center' | 'right';
    fixed?: boolean | string;
    isShow?: boolean;
    isSort?: boolean;
    sorter?: Function;
    titles?: Array<string>;
    keys?: Array<string>;
  }

  const columns = ref<Column[]>([]);

  const formFieldList = ref<Column[]>([]);

  http.request<Form, Response<Form>>('/api/form/get/-by-/name/' + formName, 'get').then(res => {
    form.value = res.data;
    getFormField();
    getFormData();
  });

  const getFormField = () => {
    http.request<FormField[], Response<FormField[]>>('/api/formField/formId/' + form.value.id, 'get').then(res => {
      const { code, data } = res;
      if (code === 200) {
        formFieldList.value = data.map(item => {
          return {
            id: item.id,
            formId: item.formId,
            title: item.title,
            key: item.name,
            dataIndex: item.name,
            type: item.type,
            formula: item.formula,
            required: item.required,
            fixed: item.fixed,
            align: 'center',
            width: 120,
            isShow: item.isShow,
            isSort: item.isSort,
            sorter: item.isSort ?
              (a, b) => a[item.name] - b[item.name] ||
                a[item.name]?.length - b[item.name]?.length ||
                new Date(a[item.name]).getTime() - new Date(b[item.name]).getTime() :
              null,
          };
        });
        generateColumns();
        generateFormulaOptions();
      }
    });
  };

  const generateFormulaOptions = () => {
    formulaOptions.value = formFieldList.value.filter(item => item.type === 'number').map(item => {
      return {
        value: item.key,
        label: item.title,
      };
    });
  };

  const generateColumns = () => {
    columns.value = formFieldList.value.filter(field => {
      return field.isShow;
    });
    columns.value.push(
      {
        title: '操作',
        align: 'center',
        fixed: 'right',
        dataIndex: 'operation',
        width: 200,
        isShow: true,
        isSort: false,
        key: 'operation',
      },
    );
    columns.value.unshift(
      {
        title: '行数',
        align: 'center',
        fixed: 'left',
        width: 80,
        dataIndex: 'index',
        isShow: true,
        isSort: true,
        sorter: (a, b) => a.index - b.index,
        key: 'index',
      },
    );
  };

  const size = ref(10);
  const current = ref(1);
  const total = ref(0);

  interface Page<T> {
    records: T[];
    total: number;
    current: number;
  }

  const getFormData = () => {
    http.request<Page<any>, Response<Page<any>>>('/api/formData/' + form.value.id, 'get', {
      current: current.value,
      size: size.value,
      keyword: keyword.value,
    }).then(res => {
      const { code, data } = res;
      if (code === 200) {
        dataList.value = data.records.map((item, index) => {
          let obj = {};
          item.map(i => {
            obj[i.fieldName] = i.value;
            obj['rowId'] = i['rowId'];
            obj['key'] = i['rowId'];
            obj['index'] = index + 1;
          });
          return obj;
        });
        total.value = data.total;
        let obj = {
          index: '合计',
        };
        columns.value.filter(item => item.type === 'number' || item.type === 'formula').map(item => {
          let sum = dataList.value.filter(data => data[item.key]).map(data => data[item.key]).reduce((a, b) => (a + Number(b)), 0);
          obj[item.key] = sum.toFixed(2);
        });
        dataList.value.push(obj);
      }
    });
  };

  const loading = ref<boolean>(false);

  interface FormField {
    id?: number;
    formId?: number;
    name: string;
    title: string;
    type: string;
    formula: string;
    required: boolean;
    fixed?: string;
    isSort?: boolean;
    isShow?: boolean;
  }

  const showFieldForm = ref<Boolean>(false);

  // 新增字段
  const addField = () => {
    showFieldForm.value = true;
  };

  const fieldFormRef = ref<FormInstance>();
  const fieldForm = ref<FormField>({
    name: '',
    title: '',
    type: 'text',
    formula: '',
    required: false,
  });
  const submitFieldForm = () => {
    fieldFormRef.value.validate().then(() => {
      fieldForm.value.formId = form.value.id;
      http.request<String, Response<String>>('/api/formField', 'post_json', fieldForm.value).then(res => {
        const { code } = res;
        if (code === 200) {
          message.success(res.msg);
          showFieldForm.value = false;
          getFormField();
          fieldFormRef.value.resetFields();
        }
      });
    });
  };
  const cancelFieldForm = () => {
    showFieldForm.value = false;
    fieldFormRef.value.resetFields();
  };

  const title = ref<string>('新增数据');

  // 新增数据
  const addData = () => {
    restDataForm();
    fileList.value = {};
    title.value = '新增数据';
    showDataForm.value = true;
  };
  // 编辑数据
  const editData = (record: { [x: string]: any; }) => {
    title.value = '编辑数据';
    restDataForm();
    fileList.value = {};
    showDataForm.value = true;
    formFieldList.value.forEach(field => {
      if (field.key !== 'operation' && field.key !== 'index' && field.key !== 'createUser' && field.key !== 'createTime' && field.key !== 'updateUser' && field.key !== 'updateTime') {
        record[field.key] = record[field.key] === '' ? null : record[field.key];
        dataForm.value[field.key] = record[field.key];
        if (field.type == 'file') {
          fileList.value[field.key] = record[field.key]?.split(',').map(i => {
            return { name: i, response: { msg: i }, thumbUrl: 'api/upload/' + i };
          });
        }
      }
      dataForm.value['rowId'] = record['rowId'];
    });
  };

  const restDataForm = () => {
    dataForm.value = {};
  };

  const showDataForm = ref<boolean>(false);
  const dataFormRef = ref<FormInstance>();
  const dataForm = ref({});
  const submitDataForm = () => {
    dataFormRef.value.validate().then(() => {
      title.value === '新增数据' ? addFormData() : updateFormData();
    });
  };
  const cancelDataForm = () => {
    showDataForm.value = false;
    restDataForm();
    fileList.value = {};
  };

  const addFormData = () => {
    const arr = Object.entries(dataForm.value).map(([key, value]) => {
        return { key, value };
      },
    );
    const addData = {
      formId: form.value.id,
      data: arr,
    };
    http.request<String, Response<String>>('api/formData', 'post_json', addData).then(res => {
      const { code, msg } = res;
      if (code === 200) {
        message.success(msg);
        showDataForm.value = false;
        restDataForm();
        fileList.value = {};
        getFormData();
        return;
      }
      message.error(msg);
    });
  };

  const updateFormData = () => {
    let rowId: any;
    const arr = Object.entries(dataForm.value).filter(([key, value]) => {
      if (key === 'rowId') {
        rowId = value;
        return false;
      }
      return true;
    }).map(([key, value]) => {
        return { key, value };
      },
    );
    const updateData = {
      formId: form.value.id,
      rowId,
      data: arr,
    };
    http.request<String, Response<String>>('api/formData', 'put_json', updateData).then(res => {
      const { code, msg } = res;
      if (code === 200) {
        message.success(msg);
        showDataForm.value = false;
        restDataForm();
        getFormData();
        fileList.value = {};
        return;
      }
      message.error(msg);
    });
  };

  const deleteData = (record) => {
    http.request<String, Response<String>>('api/formData/' + record['rowId'], 'delete').then(res => {
      const { code, msg } = res;
      if (code === 200) {
        message.success(msg);
        getFormData();
        return;
      }
      message.error(msg);
    });
  };

  const dataList = ref([]);

  type Key = string | number;

  const state = reactive<{
    selectedRowKeys: Key[];
    loading: boolean;
  }>({
    selectedRowKeys: [],
    loading: false,
  });

  const onSelectChange = (selectedRowKeys: Key[]) => {
    state.selectedRowKeys = selectedRowKeys;
  };

  const getCheckboxProps = (record) => ({
    disabled: record.index === '合计', // Column configuration not to be checked
    name: record.name,
  });

  const showFieldSetting = ref<boolean>(false);

  const fieldSetting = () => {
    showFieldSetting.value = true;
  };

  const switchFieldShow = (item: Column) => {
    columns.value.push({});
    columns.value.pop();
    item.isShow = !item.isShow;
    generateColumns();
    updateFormField(item);
  };

  const switchFieldSort = (item: Column) => {
    columns.value.push({});
    columns.value.pop();
    item.isSort = !item.isSort;
    item.sorter = item.isSort ? item.sorter ? item.sorter : (a, b) => a[item.key] - b[item.key] || a[item.key].length - b[item.key].length : null;
    generateColumns();
    updateFormField(item);
  };

  const deleteField = (id: number) => {
    http.request<String, Response<String>>('api/formField/' + id, 'delete').then(res => {
      const { code, msg } = res;
      code == 200 ? message.success(msg) : message.error(msg);
      getFormField();
    });
  };

  const updateFormField = (item: Column) => {
    http.request<String, Response<String>>('/api/formField', 'put_json', {
      id: item.id,
      name: item.key,
      title: item.title,
      fixed: item.fixed,
      isShow: item.isShow,
      isSort: item.isSort,
      type: item.type,
    }).then(res => {
      const { code } = res;
      if (code === 200) {
        // message.success(res.msg);
        // fixme 可以不用刷新字段
        // getFormField();
      }
    });
  };

  const batchDelete = () => {
    if (state.selectedRowKeys.length <= 0) {
      message.warning('未选择数据');
      return;
    }
    http.request<String, Response<String>>('api/formData/batchDelete', 'post_json', state.selectedRowKeys).then(res => {
      const { code, msg } = res;
      if (code !== 200) {
        message.error(msg);
        return;
      }
      message.success(msg);
      getFormData();
    });
  };
  const tableChange: TableProps['onChange'] = (pagination) => {
    current.value = pagination.current;
    getFormData();
  };

  const fileList = ref({});

  const beforeUpload = () => {
    return true;
  };

  type CustomRequestOption = {
    file: UploadFile;
    fileList: UploadFile[];
    onChange: (info: UploadChangeParam) => void;
    onProgress?: (e: ProgressEvent) => void;
    onSuccess?: (response: any, xhr?: XMLHttpRequest) => void;
    onError?: (error: any, xhr?: XMLHttpRequest) => void;
  };

  type CustomRequestHandler = (option: CustomRequestOption) => void

  const customRequest: CustomRequestHandler = (option) => {
    const { file, onSuccess } = option;
    const formData = new FormData();
    formData.append('file', file);
    http.post<String, Response<String>>(
      'api/file/upload',
      formData,
      { headers: { 'Content-Type': 'multipart/form-data' } },
    ).then((res) => {
      const { code } = res;
      if (code === 200) {
        onSuccess(res);
      }
    });
  };

  const handleFileChange = (info: UploadChangeParam, field: Column) => {
    if (info.file.status === 'done' || info.file.status === 'removed') {
      let fileNameList = '';
      info.fileList.forEach(item => {
        fileNameList = fileNameList + item.response.msg + ',';
      });
      dataForm.value[field.key] = fileNameList.slice(0, -1);
    }
  };

  // 导出
  const transData = (columns: Column[], tableList: any[]) => {
    const obj = columns.reduce((acc, cur) => {
      if (!acc.titles && !acc.keys) {
        acc.titles = [];
        acc.keys = [];
      }
      acc.titles.push(cur.title);
      acc.keys.push(cur.key);
      return acc;
    }, {});
    const tableBody = tableList.map(item => {
      return obj.keys.map(key => item[key]);
    });
    return [obj.titles, ...tableBody];
  };
  const exportData = () => {
    const tableData = transData(formFieldList.value, dataList.value);
    // 将一组 JS 数据数组转换为工作表
    const ws = XLSX.utils.aoa_to_sheet(tableData);
    // 创建 workbook
    const wb = XLSX.utils.book_new();
    // 将 工作表 添加到 workbook
    XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
    // 将 workbook 写入文件
    XLSX.writeFile(wb, 'table.xlsx');
  };


  const compute = computed(() => (field: Column) => {
    let formula = '';
    field.formula.split(/[\[\]]/).filter(item => item !== '').forEach(item => {
      if (/[A-Za-z]/.test(item)) {
        formula += dataForm.value[item] ? dataForm.value[item] : 0;
      } else {
        formula += item;
      }
    });
    let value = eval(formula).toFixed(2);
    dataForm.value[field.key] = value;
    return value;
  });
  const formulaOptions = ref([]);

  const onSelect = () => {
    fieldForm.value.formula += ']';
  };

  const formatName = (name) => {
    name.target.value = name.target.value.replace(/[^\a-zA-Z_]/g, '');
    fieldForm.value.name = name.target.value;
  };

  const keyword = ref('');
</script>
<template>
  <!-- 数据表格 -->
  <div class="">
    <a-table
      :row-selection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange,getCheckboxProps:getCheckboxProps }"
      :pagination="{ pageSize: size+1, current: current, total: total }"
      :columns="columns"
      :data-source="dataList"
      :scroll="{ x: 1500, y: '100vh' }"
      @change="tableChange"
    >
      <template #title>
        <div class="flex justify-between">
          <a-space>
            <a-button v-auth:admin.hide @click="addField">
              <template #icon>
                <AppstoreAddOutlined />
              </template>
              新增字段
            </a-button>
            <a-button @click="fieldSetting">
              <template #icon>
                <ToolOutlined />
              </template>
              字段设置
            </a-button>
            <a-button @click="batchDelete" type="primary" danger>
              <template #icon>
                <DeleteOutlined />
              </template>
              批量删除
            </a-button>
            <a-input-search
              placeholder='请输入关键字'
              v-model:value="keyword"
              @search="getFormData" />
          </a-space>
          <a-space>
            <a-button type="primary" @click="addData">
              <template #icon>
                <PlusOutlined />
              </template>
              新增数据
            </a-button>
            <a-button type="primary" @click="exportData">
              <template #icon>
                <ExportOutlined />
              </template>
              导出
            </a-button>
          </a-space>
        </div>
      </template>
      <template #headerCell="{ column }">
        <template v-if="column.dataIndex === ''">

        </template>
      </template>

      <template #bodyCell="{ column, record }">
        <template v-if="column.type === 'file'">
          <template v-for="item in record[column.key]?.split(',')">
            <a-image-preview-group
              v-if="item.endsWith('.jpg') || item.endsWith('.png') || item.endsWith('.jpeg') || item.endsWith('.gif')">
              <a-image
                :width="50"
                :height="50"
                placeholder="true"
                :src="'api/upload/'+item" />
            </a-image-preview-group>
            <div v-else>
              <a :href="'api/upload/'+item">{{ item.split('-NAME-')[1] }}</a>
              <br>
            </div>
          </template>
        </template>
        <template v-if="column.dataIndex === 'operation' && record.index !== '合计'">
          <a-button class="text-xs" type="primary" size="small" @click="editData(record)">编辑</a-button>
          <a-popconfirm title="确认删除？" @confirm="deleteData(record)">
            <!-- 加权限 v-auth:admin-->
            <a-button class="text-xs ml-base" danger size="small">删除</a-button>
          </a-popconfirm>
        </template>
      </template>
    </a-table>
  </div>
  <!-- 新增字段 -->
  <a-modal :okButtonProps="{ loading }" v-model:visible="showFieldForm" title="新增字段"
           @ok="submitFieldForm" @cancel="cancelFieldForm">
    <a-form ref="fieldFormRef" :model="fieldForm" :labelCol="{ span: 5 }" :wrapperCol="{ span: 16, offset: 1 }">
      <a-form-item required name="name" label="字段名">
        <a-input v-model:value="fieldForm.name" @input="formatName" />
      </a-form-item>
      <a-form-item required name="title" label="字段标题">
        <a-input v-model:value="fieldForm.title" />
      </a-form-item>
      <a-form-item required name="type" label="字段类型">
        <a-select v-model:value="fieldForm.type">
          <a-select-option value="text">文本</a-select-option>
          <a-select-option value="number">数字</a-select-option>
          <a-select-option value="date">日期</a-select-option>
          <a-select-option value="time">时间</a-select-option>
          <a-select-option value="file">文件</a-select-option>
          <a-select-option value="formula">公式</a-select-option>
        </a-select>
      </a-form-item>
      <a-form-item v-if="fieldForm.type === 'formula'" label="公式">
        <a-mentions v-model:value="fieldForm.formula" autofocus rows="3" :prefix="['[']"
                    split=""
                    @select="onSelect"
                    :options="formulaOptions" />
      </a-form-item>
      <a-form-item required name="required" label="是否必填">
        <a-select v-model:value="fieldForm.required">
          <a-select-option :value=true>是</a-select-option>
          <a-select-option :value=false>否</a-select-option>
        </a-select>
      </a-form-item>
    </a-form>
  </a-modal>
  <!-- 新增数据 -->
  <a-modal :okButtonProps="{ loading }" v-model:visible="showDataForm" :title="title"
           @ok="submitDataForm" @cancel="cancelDataForm" :width="800">
    <a-form ref="dataFormRef" :model="dataForm" :labelCol="{ span: 5 }" :wrapperCol="{ span: 16, offset: 1 }">
      <template v-for="field in formFieldList" :key="field.key">
        <a-form-item
          v-if="field.key !== 'operation' && field.key !== 'rowId' && field.key !== 'index' && field.key !== 'createUser' && field.key !== 'createTime' && field.key !== 'updateUser' && field.key !== 'updateTime'"
          :required='field.required'
          :name="field.dataIndex"
          :label="field.title">
          <a-upload v-if="field.type ===  'file'"
                    v-model:file-list="fileList[field.key]"
                    :beforeUpload="beforeUpload"
                    :customRequest="customRequest"
                    list-type="picture"
                    @change="(info:UploadChangeParam)=>handleFileChange(info,field)"
          >
            <a-button>
              <UploadOutlined></UploadOutlined>
              点击上传
            </a-button>
          </a-upload>
          <a-input v-else-if="field.type === 'formula'" :value="compute(field)" disabled />
          <a-input v-else :type="field.type" v-model:value="dataForm[field.key]" />
        </a-form-item>
      </template>
    </a-form>
  </a-modal>

  <a-modal v-model:visible="showFieldSetting" title="字段设置" footer="" :width="800">
    <div style="display: flex;flex-direction: column; text-align: center">
      <div style="display: flex;justify-content: space-between;">
        <div style="width: 200px">字段</div>
        <div style="width: 200px">显示</div>
        <div style="width: 200px">排序</div>
        <div style="width: 50px">删除</div>
      </div>
      <div v-for="(item,index) in formFieldList" :key="index"
           style="display: flex;justify-content: space-between; height: 30px">
        <div style="width: 200px">{{ item.title }}</div>
        <div style="width: 200px" @click="switchFieldShow(item)">
          <a-switch :checked="item.isShow" />
        </div>
        <div style="width: 200px" @click="switchFieldSort(item)">
          <a-switch :checked="item.isSort" />
        </div>
        <div style="width: 50px">
          <a-popconfirm title="确认删除？" @confirm="deleteField(item.id)"
                        v-if="!['createUser','createTime','updateUser','updateTime'].includes(item.key)">
            <a-button type="primary" size="small" danger>
              <template #icon>
                <DeleteOutlined />
              </template>
            </a-button>
          </a-popconfirm>
        </div>
      </div>
    </div>
  </a-modal>
</template>
